<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        // key和副作用函数的对应关系
        let data = {
            foo: 1,
            bar: 'bar'
        }
        let activeEffect;
        let effectStack = []
        function effect (fn) {
            // effectFn执行时，将其设置为当前激活的副作用函数
            const effectFn = () => {
                // 调用 cleanup 函数完成清除工作
                cleanup(effectFn)
                activeEffect = effectFn
                effectStack.push(effectFn)
                fn()
                effectStack.pop()
                activeEffect = effectStack[effectStack.length - 1]
            }
            // activeEffect.deps 用来存储所有与该副作用函数相关联的依赖集合
            effectFn.deps = []
            effectFn()
        } 
        function cleanup (effectFn) {
            for (let i = 0; i < effectFn.deps.length; i++) {
                const deps = effectFn.deps[i]
                deps.delete(effectFn)
            }
            // 最后需要重置effectFn.deps的数组
            effectFn.deps.length = 0
        }
        const bucket = new WeakMap()
        const obj = new Proxy(data, {
            get(target, key) {
                track(target, key)
                return target[key]
            },
            set(target, key, newVal) {
                target[key] = newVal
                trigger(target, key)
            }
        })
        let temp1, temp2
        effect(
            function effectFn1 () {
                // 该副作用函数执行，会执行track里面的读取操作，读取obj.foo的值，还会对obj.foo进行赋值，触发trigger，在trigger里面会把副作用函数拿出来再执行。但是当前副作用函数还没执行完毕呢，又执行一次副作用函数，就会造成无限递归
                obj.foo = obj.foo + 1
            }
        )

        function track (target, key) {
            if (!activeEffect) return
            let depsMap = bucket.get(target)
            if (!depsMap) {
                bucket.set(target, (depsMap = new Map()))
            }
            let deps = depsMap.get(key)
            if (!deps) {
                depsMap.set(key, (deps = new Set()))
            }
            // 把当前激活的副作用函数添加到依赖集合deps中
            deps.add(activeEffect)
            activeEffect.deps.push(deps)
        }

        function trigger (target, key) {
            let depsMap = bucket.get(target)
            if (!depsMap) return 
            let effects = depsMap.get(key)
            const effectsToRun = new Set(effects)
            // 防止无限递归；fn执行时会清空依赖，再把依赖加进去，这样就只会遍历effectsToRun的依赖，而effects的就删掉了
            // effectsToRun && effectsToRun.forEach(fn => fn())
            effectsToRun && effectsToRun.forEach(fn => {
                if (fn === activeEffect) {
                    return
                }
                fn()
            })
        }
    </script>
</body>
</html>